package grpc

import (
	"context"
	"fmt"
	v1etcd "gitee.com/scottq/go-framework/src/v1/libs/clients/etcd"
	"google.golang.org/grpc"
	"google.golang.org/grpc/balancer"
	"google.golang.org/grpc/balancer/base"
	"google.golang.org/grpc/resolver"
	"time"
)

type RemoteOption func(*RemoteConn) error

type RemoteConn struct {
	name        string
	addr        string
	balanceName string
	connTimeout int64
}

func NewRemoteConn(name string, addr string, options ...RemoteOption) (*grpc.ClientConn, error) {

	defer func() {
		if err := recover(); err != nil {
			panic(fmt.Errorf("%s", err))
		}
	}()
	rConn := &RemoteConn{
		name:        name,
		addr:        addr,
		balanceName: "round_robin",
		connTimeout: 10,
	}

	for _, opt := range options {
		err := opt(rConn)
		if err != nil {
			return nil, err
		}
	}

	return rConn.getClient()
}

func (conn *RemoteConn) getClient() (*grpc.ClientConn, error) {

	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(conn.connTimeout)*time.Second)
	defer cancel()

	serviceConfig := grpc.WithDefaultServiceConfig(fmt.Sprintf("{\"loadBalancingConfig\":[{\"%s\":{}}]}", conn.balanceName))

	return grpc.DialContext(ctx, conn.addr, grpc.WithInsecure(), grpc.WithBlock(), serviceConfig)
}

func OptConnTimeout(d int64) RemoteOption {
	return func(conn *RemoteConn) error {
		conn.connTimeout = d
		return nil
	}
}

func OptEtcdDiscovery(addr string) RemoteOption {
	return func(conn *RemoteConn) error {
		if addr == "" {
			addr = conn.addr
		}

		d, err := v1etcd.NewServiceDiscovery(addr, 5)
		if err != nil {
			return err
		}
		resolver.Register(d)
		b := &WeightBalance{}

		balancer.Register(base.NewBalancerBuilder(b.Name(), b, base.Config{HealthCheck: true}))

		conn.addr = fmt.Sprintf("%s://8.8.8.8/%s", d.Scheme(), conn.name)
		conn.balanceName = b.Name()
		return nil
	}
}
